#ifndef __LAB3_INCLUDE
#define __LAB3_INCLUDE

#include <cstdlib>
#include <cstdio>
#include <iostream>
#include <string>
#include <typeinfo>

#include "shape.h"
#include "circle.h"
#include "cube.h"
#include "rectangle.h"
#include "sphere.h"
#include "square.h"

using namespace std;

/**
    Returns a random integer between 1 and 11.
    Parameters: None
    Return: An integer between 1 and 11.
 */
int getRandInt();

/**
    Performs a test for a specific set of an abstract & concrete class.
    Parameters: 
        concrete B - a class that exists in abstract's heirarchy 
    Returns: None
 */
template <class abstract, class concrete>
void doTest(concrete B) {
    string name = typeid(B).name();
    cout << "\nTesting " << name.substr(1, name.length()) << " values.";
    cout << "\n**************************";
    abstract* ptr;
    ptr = &B;
    ptr->area();
    ptr->perimeter();
    ptr->volume();
    cout << "\n";
}

/**
    Constructs a test harness for a concrete class that accepts a single integer
      in its constructor.
    Parameters: None
    Returns: None
 */
template <class A, class B>
void test1Arg(void) {
    B* ptr = new B(getRandInt());
    doTest<A, B>(*ptr);
    delete ptr;
}

/**
    Constructs a test harness for a concrete class that accepts two integers
      in its constructor.
    Parameters: None
    Returns: None
 */
template <class A, class B>
void test2Arg(void) {
    B* ptr = new B(getRandInt(), getRandInt());
    doTest<A, B>(*ptr);
    delete ptr;
}


/** Entry point declaration */
int main(int, char**);

#endif